package Session;

import java.io.IOException;
import java.rmi.AccessException;
import java.rmi.NotBoundException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.registry.LocateRegistry;
import java.sql.SQLException;
import java.util.ArrayList;

import javax.sound.sampled.Line;

import org.omg.CORBA.FREE_MEM;

import com.mysql.jdbc.ResultSet;
import Agent.Agent;
import Agent.ObjectAgent;
import Exception.ExceptionFreeAgent;
import Exception.ExceptionVM;
import MachineLearning.LinearRegression;
import QueryPlan.QueryBuilderNewPlan;
import SLA.SLA;
import Server.Server;

public class Session implements Runnable{
	private ArrayList<ObjectAgent> agents;
	private Server s;
	public Integer index;
	private QueryBuilderNewPlan builder;
	//private static final Logger LOG = Logger.getLogger(Server.class.getName());
	

	public Session(Server s) throws SecurityException, IOException, ClassNotFoundException, SQLException {
		agents = new ArrayList<ObjectAgent>();
		this.s = s;
		index = 0;
		builder = new QueryBuilderNewPlan();
	}

	public ArrayList<ObjectAgent> getAgents() {
		return agents;
	}


	public void addAgent(ObjectAgent a) {
		agents.add(a);
	}

	public void run(){
		
		Remote obj;
		ObjectAgent simpleAgent;
		
		synchronized (index) {
			simpleAgent = agents.get(index);
			
		}
		try {
			Server.countThreads++;
			simpleAgent.setOn(true);
			obj = LocateRegistry.getRegistry(simpleAgent.getIp(),simpleAgent.getPort()).lookup(simpleAgent.getAgentName());
			((Agent) obj).query(simpleAgent.getTransaction(),simpleAgent.getSla(),simpleAgent.getMachine());
			
			if(agents.contains(simpleAgent)){
				s.addFreeAgent(simpleAgent);
				agents.remove(simpleAgent);
			}			
		
		} catch (ExceptionVM e) {
			
			System.out.println(e.getMessage());
			
			ArrayList<String> transaction = e.getTransaction();
			int count = e.getStillProcessed();
			int k;
			Thread t;
			
			SLA sla;
			try {
				sla = simpleAgent.getSla();
			
				builder.newPlan(transaction, sla.getQuantum());
				
				for(k=0; k<transaction.size() && count>0;k++){
					builder.setResult((ResultSet) builder.getStatement().executeQuery("explain "+transaction.get(k)));
					builder.getResult().first();
					count -= (LinearRegression.theta0 + LinearRegression.theta1*builder.getResult().getInt("rows")); 
				}
				
				if(k<transaction.size()-1){
					ArrayList<String> remainingQueries = new ArrayList<String>();
					while(k!=transaction.size()-1) remainingQueries.add(transaction.remove(k+1));
	
					ObjectAgent newAgent = s.getFreeAgent();
					System.out.println("E necessario instaciar uma nova VM!");
					agents.add(newAgent);
					
					newAgent.setTransaction(remainingQueries);
					newAgent.setSla(sla);
					t = new Thread(this);
					index = agents.size()-1;
					t.start();
					System.out.println("Valor do index do agente que ira ajudar: "+index);		
					
				}
				
				simpleAgent.setTransaction(transaction);
				t = new Thread(this);
				synchronized (index) {
					index = agents.indexOf(simpleAgent);
				}				
				System.out.println("Valor do index do agente ja ajudava: "+index);
				t.start();
				
			} catch (SQLException e1) {
				e1.printStackTrace();
			} 			
		}catch (ExceptionFreeAgent e) {
			try {
				s.addFreeAgent(simpleAgent);
				agents.remove(simpleAgent);
				
				if(!e.getTransaction().isEmpty()){
					SLA sla = simpleAgent.getSla();
					
					ObjectAgent newAgent = s.getFreeAgent();
					System.out.println("E necessario instaciar uma nova VM!");
					agents.add(0,newAgent);
					
					newAgent.setTransaction(e.getTransaction());
					newAgent.setSla(sla);
					index = 0;
					
					Thread t = new Thread(this);
					t.start();
				}
				
			} catch (AccessException e1) {
				e1.printStackTrace();
			} catch (RemoteException e1) {
				e1.printStackTrace();
			} catch (NotBoundException e1) {
				e1.printStackTrace();
			}		
			
		} catch (RemoteException e) {
			e.printStackTrace();
		} catch (NotBoundException e) {
			e.printStackTrace();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}finally{
			Server.countThreads--;
		}
		
	}
	
}
